Thymeleaf 표현식과 문법

✒️ 2025-06-24 17:44 내용 수정


참고 자료 : Thymeleaf, onlystar's 타임리프 5가지 기본 표현식/자주 쓰는 구문 정리

th 네임 스페이스

<html lang="en" xmlns:th="http://www.thymeleaf.org">

표현식

1. 변수 표현식 ${}*{}

<!-- member의 이름 출력 -->
<p th:text="${member.name}"></p>
<!-- member.name, member.email -->
<div th:object="${member}">
	<p th:text="*{name}">
	<p th:text="*{email}">
</div>

2. URL 표현식 @{}

<a href="boardList.html" th:href="@{/board/list}">board</a>
<!-- /order/1 -->
<a th:href="@{'/order/' + ${id}}">주문 상세로 이동</a>

<!-- bno 값을 path variable에 넣기 -->
<a href="board.html" th:href="@{/board/{bno}/read(bno=${bno})}">link</a>
<!-- 파라미터나 중간에 경로를 포함한 URL 생성 가능 -->
<a href="board.html" th:href="@{/board/read(bno=${bno}, type='L')}">read</a>

3. 텍스트 표현식 #{}

<p th:text="#{welcome}">Welcome</p>
<p th:text="#{greeting}">Greeting</p>

설정 및 예제

# application.properties
spring.message.encoding=UTF-8

thymeleaf_message_expression.png

# src/main/resources/messages.properties
welcome=환영합니다  
greeting=안녕하세요
<p th:text="#{welcome}">Welcome</p>
<p th:text="#{greeting}">Greeting</p>

thymeleaf_message_expression 2.png


4. 조각 표현식 ~{}

<div th:insert="~{path/fileName :: fragName}"></div>

<div th:insert="~{fragments/header :: title}"></div>

5. 텍스트 대체 표현식 ||

<!-- Hello, 이름 -->
<p th:text="|Hello, ${user.name}|">Hello</p>

6. 인라인 표현식(inline expression)

참고 자료 : Thymeleaf Inlining

<p>Hello, [[${user.name}]]</p>

<!-- 위와 같은 결과 -->
<p th:text="'Hello, ' + ${user.name}"></p>
/*[[${user.name}]]*/ "Default User"

1) Javascript inlining

<script type="text/javascript" th:inline="javascript">
	let userName = [[${user.name}]];
	console.log(userName);
</script>
<script type="text/javascript" th:inline="javascript">
	let userName = /*[[${user.name}]]*/ "testname";
	console.log(userName);
</script>

2) CSS inlining

// 데이터 형식 예시
classname = 'box'
align = 'center'
<style th:inline="css">
    .[[${classname}]] {
      text-align: [[${align}]];
    }
</style>
<!-- 결과 -->
<style th:inline="css">
    .box {
      text-align: center;
    }
</style>
<style th:inline="css">
    .[[${classname}]] {
      text-align: /*[[${align}]]*/ left;
    }
</style>

문법

1. th:text와 th:utext

// controller
@RequestMapping("/")
public String ex01(Model model) {
	model.addAttribute("lastName", "Hong");
	model.addAttribute("firstName", "GilDong");
	return "ex01";
}
<div th:text="${lastName}">lee, 더미 데이터</div>
<div>[[${lastName}]]</div>
	
<!-- 문자열 결합과 리터럴 치환 -->
<span th:text="'My name is ' + ${lastName} + ',' + ${firstName}"></span> 
<br>
<span th:text="|My name is ${lastName} ,${firstName}|"></span> 
<br>

<!-- th:utext는 <,>를 &lt;, &gt;로 변환하지 않고 그대로 출력 -->
<span th:text="${'<b>길동, 홍</b>'}">lastName, firstName</span> 
<br>
<span th:utext="${'<b>길동, 홍</b>'}">lastName, firstName</span> 
<br>

thymeleaf 1.png


2. th:if와 th:unless

// controller
@RequestMapping("/ex02")
public String ex02(Model model) {
	ArrayList<Integer> list = new ArrayList<Integer>();
	list.add(1);
	model.addAttribute("list", list);
	model.addAttribute("userNum", 3);
	model.addAttribute("grade", "D");
	return "ex02";
}
<!-- jsp -->
<table border="1">
	<!-- 조건이 참이면 렌더링 -->
	<tr th:if="${list.size()}==0">
		<td>게시물이 없습니다.</td>
	</tr>
	<!-- 조건이 참이면 렌더링x -->
	<tr th:unless="${list.size()}==0">
		<td>게시물이 있습니다.</td>
	</tr>
</table>

thymeleaf 2.png


3. th:switch, th:case, th:block

// controller
@RequestMapping("/ex02")
public String ex02(Model model) {
	ArrayList<Integer> list = new ArrayList<Integer>();
	list.add(1);
	model.addAttribute("list", list);
	model.addAttribute("userNum", 3);
	model.addAttribute("grade", "D");
	return "ex02";
}
<!-- jsp -->
<th:block th:switch="${userNum}">
	<span th:case="1">권한1</span>
	<span th:case="2">권한2</span>
	<span th:case="3">권한3</span>
</th:block>

<div th:switch="${grade}">
	<span th:case="A">특급</span>
	<span th:case="B">고급</span>
	<span th:case="C">중급</span>
	<span th:case="*">기타</span>
</div>

thymeleaf 3.png


4. th:each

// controller
@RequestMapping("/ex03")
public String ex03(Model model) {
	ArrayList<String> list = new ArrayList<String>();
	list.add("apple");
	list.add("바나나");
	list.add("딸기");
	list.add("grape");
	list.add("자두");
	model.addAttribute("list", list);
	
	return "ex03";
}
<!-- jsp -->
<select multiple>
	<option th:each="opt:${list}" th:value="${opt}">[[${opt}]]</option>
</select>

<div>
<th:block th:each="opt:${list}">
	<input type="checkbox" th:value="${opt}">[[${opt}]]
</th:block>
</div>

thymeleaf 4.png

state 객체 속성 설명
index 0부터 시작하는 index 값
count 1부터 개수를 카운트
size 요소의 개수
current 현재 요소를 알려줌
even 현재 반복이 짝수번째인지 boolean으로 알려줌
odd 현재 반복이 홀수번째인지 boolean으로 알려줌
first 현재 반복이 첫 번째인지 boolean으로 알려줌
last 현재 반복이 마지막인지 boolean으로 알려줌
<!-- jsp -->
<select multiple>
	<option th:each="opt, status:${list}" th:value="${opt}">
	[[${status.index}]] / [[${opt}]] / [[${status.even}]]
	</option>
</select>

thymeleaf 5.png


5. th:object

// controller
@RequestMapping("/ex04")
public String ex04(Model model) {
	model.addAttribute("user", new UserDTO("길동", 40));
	return "ex04";
}
<!-- jsp -->
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body th:object="${user}">
	<h1>회원 정보 출력 예제</h1>
	<div>
		<p>이름 : <span th:text="*{name}"></span></p>
		<p>나이 : <span th:text="*{age}"></span></p>
	</div>
</body>
</html>

thymeleaf 6.png


6. th:attr, th:attrappend, th:attrprepend

<img src="images/dummy.png" th:attr="src=@{images/cat.png}">
<img src="images/dummy.png" th:src="@{images/cat.png}">

<input type="text" th:value="${member.email}" placeholder="이메일">
<!-- th:attrappend : 속성 뒤에 속성값 추가 -->
<input type="button" value="Go" class="btn" th:attrappend="class=${''+style}"/>

7. th:field

@Controller
public class TestController {
	@PostMapping("/add")
	public String add(
		@ModelAttribute ProductDTO dto,
		Model model
	) {
		// form으로 받은 데이터가 자동으로 ProductDTO에 바인딩됨
		// 바인딩된 객체를 새 페이지에 Model 객체에 바인딩
		model.addAttribute("product", dto);
		return "success"; // success.html
	}
}
<form th:action="@{/add}" th:object="${productDTO}" 
method="post">
	<!-- name을 productDTO의 productName으로 자동 설정 -->
	<input th:type="text" th:field="*{name}" 
	placeholder="상품 이름">
	<input th:type="text" th:field="*{stock}" 
	placeholder="수량">
</form>
<input type="text"
	id="name"
	name="name"
	value="객체 기존값"
>

8. th:replace와 th:insert

<div th:insert="~{path/filename :: fragName}"></div>
<div th:replace="~{path/filename :: fragName}"></div>
<!-- header.html -->
<div th:fragment="simple" 
style="border: 1px solid red;">  
    <h2>헤더 조각입니다.</h2>  
</div>
<!-- test.html -->
<div th:replace="~{fragment/header :: simple}"  
     style="border: 1px solid orange;">대체하기</div>  
<div th:insert="~{fragment/header :: simple}"  
     style="border: 1px solid orange;">추가하기</div>

thymeleaf_fragment 1.png


utility 객체

타입 객체
문자열 #strings, #numbers
날짜, 시간 #dates, #calendars, #temporals
배열, 컬렉션 #arrays, #lists, #sets, #maps

기본객체(지원x)

// controller
@GetMapping("/ex03")
public String ex03(Model model, HttpServletRequest request) {
	request.setAttribute("year", 2024);
	HttpSession session = request.getSession();
	session.setAttribute("id", "asdf");
	ServletContext application = session.getServletContext();
	application.setAttribute("email", "service@gmail.com");
	
	return "ex03";
}
 <h1 th:text="|year : ${year}|"></h1>
 <h1 th:text="|id : ${session.id}|"></h1>
 <h1 th:text="|email : ${application.email}|"></h1>
	 
<!-- 더 이상 request, session, servletContext 기본 객체를 지원하지 않음 -->
<!-- 	 <h1 th:text="${#request.getAttribute('year')}"></h1> -->
<!-- 	 <h1 th:text="${#session.getAttribute('id')}"></h1> -->
<!-- 	 <h1 th:text="${#servletContext.getAttribute('email')}|"></h1> -->